// Author: Dmitry Orlov (dnorlov@google.com), 2010

#ifndef ASSEMBLER_COMMAND_
#define ASSEMBLER_COMMAND_

namespace assembler {

enum CommandType {
  INVALID_COMMAND = 0,  // No action, not used.
  MOV,          // Move
  // Arithmetical commands:
  ADD,
  SUB,
  MULT,
  DIV,
  // Function commands:
  CALL,
  RET,
  // Stack commands:
  PUSH,
  POP,
  NUM_COMMAND_TYPE  // Should be last.
};

// Possible argument types. Could be used in bit mask.
enum ArgumentType {
  NO_ARGUMENT = 0,
  ARGUMENT_REGISTER = 1,
  ARGUMENT_CONSTANT = 2,
  ARGUMENT_FUNCTION_NAME = 4
};

struct CommandArgument {
  ArgumentType type;

  union {
    // Register id, for type == ARGUMENT_REGISTER.
    int register_id;

    // Constant value, for type == ARGUMENT_CONSTANT.
    double constant_value;

    // Function name, for type == ARGUMENT_FUNCTION_NAME.
    struct {
      const char *name;
      int length;
    } function;
  } arg;
};

class CommandArgumentInfo {
 public:
  static const int ArgumentCount[NUM_COMMAND_TYPE];

  static const int Arguments[NUM_COMMAND_TYPE][2];
};

// Class command represents the whole assembler command. It checks that argument
// types properly correspond to the possible types of this command.
//
// If argument types do not go well with the command type, class constructor
// will set command type to INVALID_COMMAND
class Command {
 public:
  // For no-argument commands.
  explicit Command(CommandType type);

  // For single-argument commands.
  Command(CommandType, const CommandArgument &arg);

  // For double-argument commands.
  Command(CommandType, const CommandArgument &arg1,
          const CommandArgument &arg2);

  CommandType type() const {
    return type_;
  }

  bool is_valid() const {
    return (type_ != INVALID_COMMAND);
  }

  const CommandArgument &arg1() const {
    return arg1_;
  }

  const CommandArgument &arg2() const {
    return arg2_;
  }

 private:
  CommandType type_;

  CommandArgument arg1_;
  CommandArgument arg2_;
};

}  // namespace assember

#endif